import java.util.*;

class MapCreator{
	
	
	private Random rm = new Random();
	
	private ArrayList<WalkTile> walk;
	private ArrayList<WallTile> wall;
	
	private int x;
	private int y;
	private double opr;
	private int rmtp;
	private int avgrmsz;
	double rtocr;
	private int[] rooms;
	
	private int[][] availableTiles;
	
	
	public MapCreator(){}
	
	/*
	2D integer list with each tile having the following code:
	Unwalkable tile:	Stationary object = -2
	Unwalkable tile: Wall = -1
	
	Unformatted tile = 0;
	
	Walkable tile = 1
	Walkable tile with door = 2
	Walkable tile with loot = 3
	Walkable tile with monster = 4
	
	Portal tile to lower level = 5
	Portal tile to higher level = 6
	
	*/
	public ArrayList<Tile>  createMap(MapPreferences mps, int dlvl){
		gather(mps);
		if( dlvl % 5 != 0 && dlvl % 10 != 0){
			constructBase();
		}
		else if( dlvl % 5 == 0 && dlvl % 10 != 0){
			// boss encounter lvl
		}
		else if( dlvl % 10 == 0){
			// big boss encounter lvl
		}
		
	}
	
	private void gather(MapPreferences){
		// Level length
		x = (int)(rm.nextInt(mps.getW() * 0.25)) + mps.getW() * 0.75;
		// Level height
		y = (int)(rm.nextInt(mps.getH()  * 0.25))  + mps.getH() * 0.75;
		// Openess ratio
		opr = mps.getOpenessRatio();
		// Room type, spesified later
		rmtp = 0 //mps.getTheme().getRoomType(),
		// Average room size
		avgrmsz = mps.getAvgRoomSize()
		// Room to Corridor ratio
		rtocr = mps.getRtoCRatio
	}
	
	public ArrayList<Tile> constructBase(){
		// All walkable tiles in this spesific level
		int walkableCount = x*y*opr
		walk = new ArrayList<WalkTile>(walkableCount);
		
		int roomTileCount = (int)(walkableCount * rtocr);
		int corridorTileCount = walkableCount - roomTileCount;
		
		// Create rooms according to mps and allocate them
		getRoomLocations();
		// Build actual tiles according to the representation in rooms list
		buildRooms();
		
			
		// Size = amount of tiles needed to cover walk areas borders
		wall = new ArrayList<WallTile>();
		
	}
	
	// Return form: {room0 anchor x, room0 anchor y, room0 width, room0 height, ...}
	private void getRoomLocations(){
		// Room count
		int rmc = (int)(rtc / avgrmsz);

		int base = (int)sqrt(avgrmsz);
		
		rooms = new int[4 * rmc];
		
		int[] tmp = new int[2];
		
		// rmtp = 0, for near square shaped rooms
		if(rmtp = 0){
			for(int i = 0; i<rmc; i++){
				tmp = getQuadDims(base);
				rooms[(4 * i) + 2] = tmp[0];
				rooms[(4 * i) + 3] = tmp[1];
			}
		}
		// other shapes to follow?
		
		arrangeRooms();
	}
	
	// Returns room width and height for a quadrilateral object with A = avg +/- 0.1 * avg
	private int[] getQuadDims(int base){
		int w = 0;
		int h = 0;
		int area = 0;
		while( area > (avg * 1.1) || area < (avg * 0.9)){
			w = rm.nextInt(base * 0.6) + (base * 0.7);
			h = rm.nextInt(base * 0.6) + (base * 0.7);
			area = w * h;
		}
		return new int[] {w, h};
	}
	
	private void arrangeRooms(){
		int ax = 0;
		int ay = 0;
		boolean alloc = false;
		boolean fit = false;
		int aCount;
		while(!alloc){
			// All tiles available
			availableTiles = new int[y][x];
			alloc = true;
			// Try to allocate each tile
			for(int i = 0; i< rooms.length; i+4){
				// Try allocating each tile 50 times
				while(n < 50 && !fit){
					ax = 1 + rm.nextInt(x);
					ay = 1 + rm.nextInt(y);
					// Find coordinates that aren't already reserved
					while(availableTiles[ay][ax] == 1){
						ax = 1 + rm.nextInt(x);
						ay = 1 + rm.nextInt(y);
					}
					// Test every tile for occupancy
					for(int jy=ay; jy<ay + rooms[i+3]; jy++){
						for(int jx=ax; jx<ax + rooms[i+2]; jx++){
							if(jy < y && jx < x && availableTiles[jy][jx] != 1) aCount++;
						}
					}
					n++;
					// If required area == found free area => mark final coordinates to rooms list
					if(rooms[i+3] * rooms[i+2] == aCount){
						fit = true;
						for(int ky=ay; ky<ay + rooms[i+3]; ky++){
							for(int kx=ax; kx<ax + rooms[i+2]; kx++){
									availableTiles[ky][kx] = 1;
							}
						}
						rooms[i] = ax;
						rooms[i+1] = ay;
					}
						
				}
				// If unable to fit a room, jump out of the loop and start again
				if(n == 50 && fit != true){
					alloc = false;
					i = rooms.length
				}
			}
		}
	}
	
	private void buildRooms(){
		// Arranged rooms to WalkTile ArrayList
	}
	
	private void markBorderWalls(){
		for(int iy = 0; iy< y; iy++){
			availableTiles[iy][0] = -1;
			availableTiles[iy][x-1] = -1;
		}
		for(int ix = 0; ix< x; ix++){
			availableTiles[0][ix] = -1;
			availableTiles[y-1][ix] = -1;
		}
	}
	
	
	private void corridorize(){
		int[] sRoom = new int[4];
		int[] fRoom = new int[4];
		int fi = 0;
		
		for(int i<0; i<room.length; i=i+4){
			sRoom[0] = room[i];
			sRoom[1] = room[i+1];
			sRoom[2] = room[i+2];
			sRoom[3] = room[i+3];
			
			fi = rm.nextInt((int)room.length * 0.25);
			while(fi == i) fi = rm.nextInt((int)room.length * 0.25);
			
			fRoom[0] = room[fi];
			fRoom[1] = room[fi+1];
			fRoom[2] = room[fi+2];
			fRoom[3] = room[fi+3];
			
			ArrayList<int[]> route = findPath(getPoint(sRoom), getPoint(fRoom), mergeCoords(sRoom,fRoom));
			
			// route -> walkable tiles
		}
			
		// Select rooms
		
		// Select point
		
		// Find possible paths
		
		// Take random from prev. list
	}
	
	private int[] mergeCoords(int[] a, int[] b){
		int[] coords = [(a[2]*a[3]) + (b[2]*b[3])]
		int i = 0;
		for(int ay = 0; ay < a[3]; ay++){
			for(int ax = 0; ax < a[2]; ax++){
				coords[i] = ax;
				i++;
				coords[i] = ay;
				i++;
			}
		}
		for(int by = 0; by < b[3]; by++){
			for(int bx = 0; bx < b[2]; bx++){
				coords[i] = bx;
				i++;
				coords[i] = by;
				i++;
			}
		}
		return coords;
	}
	
	private int[] getPoint(int[] r){
		ArrayList<int> points = new ArrayList<int>();
		for(int ix = r[0]-1; ix<r[0]+r[2]+1; ix++;){
			if((r[1]-1) != 0 && ix != 0 && ix != (x-1)){
				points.add(ix);
				points.add(r[1]-1);
			}
			if((r[1] + r[3]) != 0 && ix != 0 && ix != (x-1)){
				points.add(ix);
				points.add(r[1]+r[3]);
			}
		}
		 int i = rm.nextInt((int)(points.size() * 0.5));
		return new int[] {points.get(i * 2), points.get((i * 2) + 1)};
	}
	
	// Find shortest path from start to end without using unavailable coords or borders
	// Possible variation would include waypoints
	private int[] findPath(int[] s, int[] e, int[] unavbl){
		ArrayList<int[]> paths = new ArrayList<int[]>();
		ArrayList<int[]> temp = new ArrayList<int[]>();
		
		paths.add(new int[] {s[0], s[1], 0};
		
		int tx = 0;
		int ty = 0;
		int tw = 0;
		
		boolean end = false;
		
		while(!end){
			for(int i=0;i<paths.size();i++){
				tx = paths.get(i)[0];
				ty = paths.get(i)[1];
				tw = paths.get(i)[2] + 1;
				temp = new ArrayList<int[]>();
				temp.add(new int[] {tx + 1, ty, tw});
				temp.add(new int[] {tx, ty + 1, tw)};
				temp.add(new int[] {tx - 1, ty, tw});
				temp.add(new int[] {tx, ty - 1, tw});
				end = checkAndAdd(paths,temp, unavbl, e);
			}
		}
		
		return buildRoute(paths);
	}
	
	private boolean checkAndAdd( ArrayList<int[]> p, ArrayList<int[]> t, int[] u, int[] e){
		for(int i = 0; i<t.size();i++){
			for(int j = 0;j<u.length;j+=2){
				if((t.get(i)[0] == u[j] && t.get(i)[1] == u[j+1]) || t.get(i)[0] == 0 || t.get(i)[0] == x || t.get(i)[1] == 0 || t.get(i)[1] == y){
					t.remove(i);
					i--;
					j = u.length;
				}
			}
		}
		
		for(int k = 0; k<t.size();k++){
			for(int s = 0;s<p.size();s++){
				if(t.get(i)[0] == p.get(s)[0] && t.get(i)[1] == p.get(s)[1]){
					t.remove(k);
					k--;
					s = p.size();
				}
			}
		}
		
		boolean done = false;
		for(int h=0;h<t.size();h++){
			p.add(t.get(h));
			if(t.get(i)[0] == e[0] && t.get(i)[1] == e[1]) done = true;
		}
		
		return done;
	}
	
	private ArrayList<int[]> buildRoute(ArrayList<int[]> paths){
		int f = 0;
		for(int i=0;i<paths.size();i++){
			if(paths.get(f)[2] < paths.get(i)[2]){
				f = i;
			}
		}
		
		ArrayList<int[]> route = ArrayList<int[]>();
		route.add(new int[] {paths.get(f)[0], paths.get(f)[1]});
		int k = paths.get(f)[2];
		
		while(k > 0){
			for(int j = 0; j<paths.size();j++){
				if(paths.get(j)[2] == k-1){
					route.add(new int[] {paths.get(j)[0], paths.get(j)[1]});
					k--;
					j = paths.size();
				}
			}
		}
		return route;
	}
	
	
	
	public ArrayList<Tile> populate(ArrayList<Tile> map){
		
	}
	
	// MapHost will use this to get a set of possible tiles to convert into portals
	//		-> needs to have contains false
	public ArrayList<WalkTile> getWalkTiles(){ return walk;}
	
	public ArrayList<WallTile> getWallTiles(){ return wall;}
	
	
	
}































